#include "meta_utils.h"
#include "nvdsmeta.h"
#include "nvdsinfer.h"
#include "nvdsmeta_schema.h"
#include "gstnvdsmeta.h"
#include "gstnvdsinfer.h"
#include <stdio.h>

static void print_nvds_batch_meta(GstBuffer *buffer);

void print_buffer_meta(GstBuffer *buffer) {
    GstMeta *current = NULL;
    gpointer state = NULL;
    while ((current = gst_buffer_iterate_meta(buffer, &state))) {
        const gchar *name = g_type_name(current->info->type);
        g_print("get meta type name %s\n", name);
    } 
    print_nvds_batch_meta(buffer);
}

void print_nvds_batch_meta(GstBuffer *buffer) {
    NvDsBatchMeta *batch_meta = gst_buffer_get_nvds_batch_meta(buffer);
    if (batch_meta == NULL) {
        return;
    }
    g_print("found nvds batch meta\n");
    NvDsFrameMetaList *frame_meta_list = batch_meta->frame_meta_list;
    if (frame_meta_list == NULL || batch_meta->num_frames_in_batch == 0) {
        return;
    }

    guint meta_index = 0;
    for (GList *l = frame_meta_list; l != NULL; l = l->next, meta_index++) {
        NvDsFrameMeta *frame_meta = (NvDsFrameMeta *)l->data;
        if (frame_meta == NULL) {
            continue;
        }
        g_print("  print frame_meta #%d for frame #%d\n", meta_index, frame_meta->frame_num);

        if (frame_meta->num_obj_meta == 0) {
            continue;
        }

        guint obj_meta_index = 0;
        for (GList *l = frame_meta->obj_meta_list; l != NULL; l = l->next, obj_meta_index++) {
            NvDsObjectMeta *obj_meta = (NvDsObjectMeta *)l->data;
            
            if (obj_meta == NULL) {
                continue;
            }
            g_print("    print obj_meta #%d\n", obj_meta_index);
            for (GList *c = obj_meta->classifier_meta_list; c != NULL; c = c->next) {
                NvDsClassifierMeta *c_meta = (NvDsClassifierMeta *)c->data;
                for(GList *l = c_meta->label_info_list; l != NULL; l = l->next) {
                    NvDsLabelInfo *label_info = (NvDsLabelInfo *)l->data;
                    g_print("    label_info result_class_id %d, result_label %s, result_prob %f\n", 
                        label_info->result_class_id, 
                        label_info->result_label,
                        label_info->result_prob
                    );
                }
            }
        }
    }
}

const NvDsLabelInfo *get_buffer_label(GstBuffer *buffer) {
    NvDsBatchMeta *batch_meta;
    
    if (buffer == NULL || (batch_meta = gst_buffer_get_nvds_batch_meta(buffer)) == NULL) {
        return NULL;
    }

    for (GList *f = batch_meta->frame_meta_list; f != NULL; f = f->next) {
        NvDsFrameMeta *frame_meta = f->data;
        if (frame_meta == NULL) continue;
        for (GList *o = frame_meta->obj_meta_list; o != NULL; o = o -> next) {
            NvDsObjectMeta *obj_meta = o->data;
            if (obj_meta == NULL) continue;
            for (GList *c = obj_meta->classifier_meta_list; c != NULL; c = c->next) {
                NvDsClassifierMeta *c_meta = c->data;
                for(GList *l = c_meta->label_info_list; l != NULL; l = l->next) {
                    NvDsLabelInfo *label_info  = l->data;
                    if (label_info == NULL) continue;
                    return label_info;
                }
            }
        }
    }
    return NULL;
}

const char *get_buffer_class(GstBuffer *buffer) {
    static char line_buffer[256]; 
    const NvDsLabelInfo *label_info = get_buffer_label(buffer);
    if (label_info == NULL) {
        return NULL;
    }
    sprintf(line_buffer, "%.2f, #%4d, %s", label_info->result_prob, label_info->result_class_id, label_info->result_label);
    return line_buffer;
}

TensorView get_buffer_tensor(GstBuffer *buffer) {
    NvDsBatchMeta *batch_meta;
    TensorView ret = { .dims = NULL , .data = NULL };
    
    if (buffer == NULL || (batch_meta = gst_buffer_get_nvds_batch_meta(buffer)) == NULL) {
        return ret;
    }

    for (GList *f = batch_meta->frame_meta_list; f != NULL; f = f->next) {
        NvDsFrameMeta *f_meta = f->data;
        for (GList *u = f_meta->frame_user_meta_list; u != NULL; u = u->next) {
            NvDsUserMeta *meta = u->data;
            if (meta->base_meta.meta_type == NVDSINFER_TENSOR_OUTPUT_META) {
                NvDsInferTensorMeta *tensor_meta = meta->user_meta_data;
                ret.data = tensor_meta->out_buf_ptrs_host[0];
                ret.dims = &tensor_meta->output_layers_info[0].inferDims;
                return ret;
            }
        }
    }

    return ret;
} 
